﻿using System;
using System.ServiceModel;
using nRoute.Components.Composition;
using Orktane.Components;
using Orktane.Sample.ConsoleApp.Services;
using Orktane.ScriptActionModel;

namespace Orktane.Sample.ConsoleApp.ScriptActions
{
    [MapScriptAction("START-PROCESS", "Start Process", ShortDescription = "Starts a process with the given file-name on the remote machine",
        Lifetime = InstanceLifetime.PerInstance)]
    public class StartProcessScriptAction
        : IConsoleScriptAction
    {
        private const string ECLIPSE = "...";
        private const string SPACE = " ";

        private RemoteProcessesServiceClient _remoteProcessesService;
        private IConsole _console;
        private IDisposable _completionToken;

#region IConsoleScriptAction Members

        public void Intialize(IConsole console, IDisposable completionToken)
        {
            if (console == null) throw new ArgumentNullException("console");
            if (completionToken == null) throw new ArgumentNullException("completionToken");

            _console = console;
            _completionToken = completionToken;
        }

        public void Execute(Arguments args)
        {
            if (args == null || string.IsNullOrWhiteSpace(args.CommandLine))
            {
                _console.Write("Please specify the file name of the process to start on the remote machine");
                _completionToken.Dispose();
            }
            else
            {
                _remoteProcessesService = new RemoteProcessesServiceClient();
                _remoteProcessesService.StartProcessCompleted += new EventHandler<StartProcessCompletedEventArgs>(Service_StartProcessCompleted);
                _remoteProcessesService.StartProcessAsync(args.CommandLine);
                _console.DisableInput = true;
            }
        }

        public bool Cancel()
        {
            if (_remoteProcessesService != null &&
                (_remoteProcessesService.State == CommunicationState.Opened || _remoteProcessesService.State == CommunicationState.Opening))
            {
                _remoteProcessesService.Abort();
            }

            _console.WriteLine("Cancelled Start-Process..");
            return true;
        }

        public void Close()
        {
            if (_remoteProcessesService == null) return;
            
            if (_remoteProcessesService.State != CommunicationState.Closed && _remoteProcessesService.State != CommunicationState.Closing)
            {
                _remoteProcessesService.CloseAsync();
            }
            _remoteProcessesService.StartProcessCompleted -= new EventHandler<StartProcessCompletedEventArgs>(Service_StartProcessCompleted);
            _remoteProcessesService = null;
            _console.DisableInput = false;          
        }

#endregion

#region Handlers

        private void Service_StartProcessCompleted(object sender, StartProcessCompletedEventArgs e)
        {
            if (_remoteProcessesService == null) return;

            if (!e.Cancelled && e.Error == null)
            {
                if (e.Result == null)
                {
                    _console.WriteLine("Could not start process with the given file name.");
                }
                else
                {
                    _console.WriteLine("---------------------------------------------------------------------------");
                    _console.Write(PadOrConstrainString("P.ID", 8));
                    _console.Write(PadOrConstrainString("MEMORY USE", 14));
                    _console.WriteLine(ConstrainString("PROCESS NAME", 56));
                    _console.WriteLine("---------------------------------------------------------------------------");

                    _console.Write(PadOrConstrainString(e.Result.ProcessId.ToString(), 8));
                    _console.Write(PadOrConstrainString(e.Result.WorkingMemorySize.ToString("###,###"), 14));
                    _console.WriteLine(ConstrainString(e.Result.ProcessName, 56));
                }
            }
            else
            {
                if (e.Error != null)
                {
                    _console.Write("An Error Occurred: ");
                    _console.WriteLine(e.Error.Message);
                    _console.WriteLine();
                    _console.WriteLine(e.Error.StackTrace);
                }
                else
                {
                    _console.WriteLine("Script command was cancelled");
                }
            }

            _console.WriteLine();

            // and indicate that we have completed
            _completionToken.Dispose();
        }

#endregion

#region Helpers

        private static string PadOrConstrainString(string value, int length)
        {
            if (string.IsNullOrEmpty(value)) value = string.Empty;
            if (value.Length > length) return ConstrainString(value, length);

            // we add 
            var _addedLength = length - value.Length;
            for (var i = 0; i < _addedLength; i++)
            {
                value += SPACE;
            }
            return value;
        }

        private static string ConstrainString(string value, int length)
        {
            if (string.IsNullOrEmpty(value)) return string.Empty;
            return value.Length > length ? value.Substring(0, length - 3) + ECLIPSE : value;
        }

#endregion

    }
}
